/// A string type.
#ifndef PRYN_STRING_H
#define PRYN_STRING_H

typedef struct PrynString PrynString;
typedef struct PrynState PrynState;

#include <pryn/platform.h>

/** @brief A UTF-8 string.
	@ingroup PrynString */
struct PrynString
{
	size_t length; ///< Number of bytes in the string. Note that this is not the same as the number of characters.
	char *pointer; ///< Pointer to the first element or 0 for none.

#if __cplusplus
	PrynString () : length (0), pointer (0) { } ///< Clear to an empty string.
	PrynString (const PrynString &other) : length (other.length), pointer (other.pointer) { } ///< Copy the parameters of the string without duplicating the data.
	inline PrynString (const char *text); ///< Initialise to a NUL-terminated string.

	static inline PrynString Allocate (PrynState *state, size_t length); ///< Allocate a string with this many bytes of data.

	static inline PrynString FromZMaximum (const char *pointer, size_t maximumLength); ///< Create a Pryn string from a NUL-terminated string with a maximum length.

	inline void free (PrynState *state); ///< Free the string and null the length and pointer. This will not throw an exception.

	inline const char *toZ (PrynState *state) const; ///< Convert the string into a NUL-terminated string. This first checks if the string itself is NUL-terminated; if so this will return the string pointer. Otherwise this allocates a copy and returns it.

	inline void freeZ (PrynState *state, const char *zstring) const; ///< Free @e zstring if its pointer is different than @e string's pointer. This is meant to be a complement to #toZ.

	inline bool operator == (const PrynString &other) const; ///< Whether these strings have the same length and values.
	inline bool operator == (const char *other) const; ///< Whether these strings have the same length and values.
	inline bool operator != (const PrynString &other) const; ///< Whether these strings have different lengths or values.
	inline bool operator != (const char *other) const; ///< Whether these strings have different lengths or values.

	inline bool startsWith (const PrynString &other) const; ///< @e true if the beginning of the string is equal to @e prefix or @e false if it is not.

	inline PrynString duplicate (PrynState *state) const; ///< Allocate a duplicate of this string.
	inline void overwrite (PrynState *state, const PrynString &overwrite); ///< Free the string then duplicate @e overwrite into it.

	inline PrynString join (PrynState *state, PrynString &other) const; ///< Create a string that joins the two strings together.  Even if one or the other is empty, this will return a copy.
	inline PrynString sliceToEnd (size_t offset) const; ///< Slice the string from the offset to the end. If offset is greater than the length of this string, an empty string is returned.
	inline PrynString sliceTo (size_t end) const; ///< Slice from the beginning of the string to the offset. If offset is beyond the end of the string, the entire string is returned.
#endif /* __cplusplus */
};

#include <pryn/result.h>

/** @defgroup PrynString PrynString C API
	@{ */

PrynImport (PrynResult, prynStringNull, (PrynString *result)) ///< Returns an empty string with length and pointer of 0.
PrynImport (PrynResult, prynStringAllocate, (PrynString *result, PrynState *state, size_t length)) ///< Allocate a string containing this many bytes of data.

PrynImport (PrynResult, prynStringToZ, (const PrynString *string, PrynState *state, const char **output)) ///< Convert the string into a NUL-terminated string. This first checks if the string itself is NUL-terminated; if so this will return the string pointer. Otherwise this allocates a copy and returns it. Using #prynStringFreeZ later may simplify ridding yourself of the Z-string.
PrynImport (PrynResult, prynStringFreeZ, (const PrynString *string, PrynState *state, const char *zstring)) ///< Free @e zstring if its pointer is different than @e string's pointer. This is meant to be a complement to #prynStringToZ.
PrynImport (PrynResult, prynStringJoin, (PrynString *result, PrynState *state, const PrynString *a, const PrynString *b)) ///< Create a string that joins the two strings together. Even if one or the other is empty, this will return a copy.
PrynImport (PrynResult, prynStringFree, (PrynString *string, PrynState *state)) ///< Free the string and null it.
PrynImport (PrynResult, prynStringDuplicate, (PrynString *result, PrynState *state, const PrynString *string)) ///< Allocate a duplicate of the string.
PrynImport (PrynResult, prynStringOverwrite, (PrynString *string, PrynState *state, const PrynString *overwrite)) ///< Free the string then duplicate @e overwrite into it.

PrynImport (PrynResult, prynStringEqual, (const PrynString *a, const PrynString *b)) ///< #PrynResultTrue if these strings are equivalent, #PrynResultFalse if they're not, or an error code; see #PrynResult.
PrynImport (PrynResult, prynStringStartsWith, (const PrynString *string, const PrynString *prefix)) ///< #PrynResultTrue if the beginning of @e string is equal to @e prefix, #PrynResultFalse if they're not, or an error code; see #PrynResult.

PrynImport (PrynResult, prynStringSliceToEnd, (PrynString *result, const PrynString *string, size_t offset)) ///< Slice the string from the offset to the end.
PrynImport (PrynResult, prynStringSliceTo, (PrynString *result, const PrynString *string, size_t end)) ///< Slice from the beginning of the string to the offset.

PrynImport (PrynResult, prynStringFrom, (PrynString *result, const char *pointer, size_t length)) ///< Create a Pryn string from the parameters.
PrynImport (PrynResult, prynStringFromZ, (PrynString *result, const char *pointer)) ///< Create a Pryn string from a NUL-terminated string.
PrynImport (PrynResult, prynStringFromZMaximum, (PrynString *result, const char *pointer, size_t maximumLength)) ///< Create a Pryn string from a NUL-terminated string with a maximum length.

/// Create a string from a literal string.
#define prynStringLiteral(RESULT, STRING) prynStringFrom ((RESULT), (STRING), sizeof ((STRING)) - 1)

#define prynStringLiteralBlock(LITERAL, NAME, BLOCK) do { PrynString NAME; prynStringLiteral (&NAME, (LITERAL)); BLOCK } while (0)

/** @} */ // (group)

#endif /* PRYN_STRING_H */
